/*
 * Copyright (C) 2012 Tomas Shestakov. <http://code.google.com/p/jmmo/>
 */

package jmmo.engine.property

import org.scalatest.WordSpec
import org.scalatest.matchers.ShouldMatchers

/**
 * @author Tomas Shestakov
 */
class ChangeEventSpec extends WordSpec with ShouldMatchers {

  val ObsSimple = new Observable with ObservableListenersSupport {
    val id = Identifier('name -> "ObsSimple")
    override def toString = id.attributes('name).asInstanceOf[String]
    def serializedCopy(filter: Filter) = null
  }
  
  val ObsProp = new PropertySupport[Int] {
    val id = Identifier('name -> "ObsProp")
    def isWritable = false
    def isReadable = true
    def apply() = 1
    def update(value: Int) { throw new UnsupportedOperationException }
    override def toString = id.attributes('name).asInstanceOf[String]
    def canEqual(that: Any) = true
    def alter = jmmo.util.emptyPartial[Alteration]
  }

  val ObsContainer = new ObservableContainer with ChildrenListenersSupport with SerializableSilentObservableContainerSupport {
    val id = Identifier('name -> "ObsContainer")
    def attributes = Map.empty
    def observable(ids: Identifier*) = None
    def observables = List.empty
    override def toString = id.attributes('name).asInstanceOf[String]
  }
  
  "A ChangeEvent" should {

    "not allow to create instances of inherited from ChangeEvent classes with null source" in {
      evaluating {
        AddObsEvent(null, ObsSimple)
      } should produce [IllegalArgumentException]

      evaluating {
        RemoveObsEvent(null, ObsSimple)
      } should produce [IllegalArgumentException]

      evaluating {
        ChangePropertyEvent(null, 0, 1)
      } should produce [IllegalArgumentException]
    }

    "allow to create instance of AddObsEvent" in {
       (AddObsEvent(ObsContainer, ObsSimple) match {
         case AddObsEvent(ObsContainer, ObsSimple) => true
         case _ => false
       }) should be (true)
    }

    "allow to create instance of RemoveObsEvent" in {
      (RemoveObsEvent(ObsContainer, ObsSimple) match {
        case RemoveObsEvent(ObsContainer, obsSimple) => true
        case _ => false
      }) should be (true)
    }

    "allow to create instance of ChangePropertyEvent" in {
      (ChangePropertyEvent(ObsProp, 0, 1) match {
        case ChangePropertyEvent(ObsProp, 0, 1) => true
        case _ => false
      }) should be (true)
    }

    "provide extractor to extract from AddObsEvent, RemoveObsEvent, ChangePropertyEvent, ChangeContentEvent" in {
      //tested before
    }

    "provide newChain to add `ObservableContainer` to event chain" in {
      val event = ChangePropertyEvent(ObsProp, 0, 1)
      val newEvent = event.newChain(ObsContainer)
      (newEvent match {
        case (ChangePropertyEvent(ObsProp, 0, 1, ObsContainer)) => true
        case _ => false
      }) should be (true)
    }

    "has appropriate equal method," which {

      val add1 = AddObsEvent(ObsContainer, ObsSimple)
      val add2 = AddObsEvent(ObsContainer, ObsSimple)
      val add3 = AddObsEvent(ObsContainer, ObsProp)
      val remove1 = RemoveObsEvent(ObsContainer, ObsSimple)
      val remove2 = RemoveObsEvent(ObsContainer, ObsSimple)
      val remove3 = RemoveObsEvent(ObsContainer, ObsContainer)
      val cve1 = ChangePropertyEvent(ObsProp, 0, 1)
      val cve2 = ChangePropertyEvent(ObsProp, 0, 1)
      val cve3 = ChangePropertyEvent(ObsProp, 2, 1)

      "returns true on compare events of same type with same parameters" in {
        add1 == add2 should be (true)
        remove1 == remove2 should be (true)
        cve1 == cve2 should be (true)
      }

      "returns false on compare any different type of events" in {
        add1 == remove1 should be (false)
        add1 == add3 should be (false)
        add3 == cve1 should be (false)
        remove2 == add2 should be (false)
        remove2 == remove3 should be (false)
        cve1 == cve3 should be (false)
        cve1 == add3 should be (false)
      }
    }

    "has appropriate toString method" in {
      val add = AddObsEvent(ObsContainer, ObsSimple)
      val remove = RemoveObsEvent(ObsContainer, ObsSimple)
      val cve = ChangePropertyEvent(ObsProp, 0, 1)

      info(add.toString)
      info(remove.toString)
      info(cve.toString)

      add.toString should be ("AddObsEvent(ObsContainer,ObsSimple,chain())")
      remove.toString should be ("RemoveObsEvent(ObsContainer,ObsSimple,chain())")
      cve.toString should be ("ChangePropertyEvent(ObsProp,0,1,chain())")
    }
 }

}
